Replication of Cross-sex mQTL

library(dbplyr)
library(DBI)
ntests <- 126904049
con <- dbConnect(RSQLite::SQLite(), "terre_and_digpd_cis_all_impute_mQTL_9_methy_PC.db")
mQTL_results <- tbl(con, "terre")
ntests_digpd <- 100363748
mQTL_results_digpd <- tbl(con, "digpd")
mQTL_results %>%
  filter(FDR < 0.05) %>%
  summarize(total = n(), cpgs = n_distinct(gene))
mQTL_results %>%
  filter(`p-value` < (0.05 / ntests)) %>%
  summarize(total = n(), cpgs = n_distinct(gene))

mQTL_results_digpd %>%
  filter(FDR < 0.05) %>%
  summarize(total = n(), cpgs = n_distinct(gene))
mQTL_results_digpd %>%
  filter(`p-value` < (0.05 / ntests)) %>%
  summarize(total = n(), cpgs = n_distinct(gene))

library(dbplot)
mQTL_results %>% dbplot_histogram(`p-value`)
# qq(mQTL_results$`p-value`)

Checking replication

\(\pi_1\) statistics

to_check_replication <- mQTL_results_digpd %>%
  inner_join(mQTL_results %>% filter(FDR < 0.05), by = c("SNP", "gene"), copy = TRUE) %>%
  collect() %>% as.data.table()

pi0res <- pi0est(na.omit(as.numeric(to_check_replication$`p-value.x`)), lambda = seq(0, 0.245, 0.005), pi0.method = "bootstrap")
1 - pi0res$pi0
plot(pi0res$pi0.lambda, pi0res$lambda, xlab = bquote(pi[0]), ylab = bquote(lambda))
abline(v = pi0res$pi0)
qvalres <- qvalue_truncp(na.omit(as.numeric(to_check_replication$`p-value.x`)))
1 - qvalres$pi0
abline(v = qvalres$pi0)
pi0est(as.numeric(to_check_replication$`p-value.x`) / 0.25)

Colocalization with parkinson’s loci and overlap with ewas hits

load("../prs_analyses/prs_nalls_cross_w_sex_stratified.RData")
manifest <- IlluminaHumanMethylationEPICanno.ilm10b4.hg19::Other %>%
  as.data.frame() %>%
  rownames_to_column(var = "name")

positions <- IlluminaHumanMethylationEPICanno.ilm10b4.hg19::Locations %>%
  as.data.frame() %>%
  rownames_to_column(var = "name")

cross-sex GWAS

cross_coloc <- fread("cross_mqtl_cross_sumstats_pd_snp_colocalization_ph4.txt.gz")
male_coloc <- fread("male_mqtl_cross_sumstats_pd_snp_colocalization_ph4.txt.gz")
female_coloc <- fread("female_mqtl_cross_sumstats_pd_snp_colocalization_ph4.txt.gz")

Cross-sex mQTL

sig_bonf_cross <- top_prs_hits %>% filter(p.adjust(P.Value,method="BH") < 0.05)
cross_coloc %>%
  filter(PP.H4.abf > 0.9,probe %in% sig_bonf_cross$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

cross_coloc_probes <- cross_coloc %>%
  filter(PP.H4.abf > 0.9,probe %in% sig_bonf_cross$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name")) %>% select(probe)

cross_coloc %>%
  filter(PP.H4.abf < 0.9,probe %in% sig_bonf_cross$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

cross_coloc %>%
  filter(PP.H4.abf > 0.9,!probe %in% sig_bonf_cross$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))
(non_colocalized_cross <- sig_bonf_cross %>% left_join(manifest,by=c("ID" = "name"))  %>% left_join(positions,by=c("ID"="name")) %>% filter(!ID %in% cross_coloc_probes$probe) %>% arrange(chr,pos)  %>% select(ID,UCSC_RefGene_Name,chr,pos))

non_colocalized_cross %>% count(gsub(";.*","",UCSC_RefGene_Name))

Male mQTL

sig_bonf_male <- top_male_prs_hits %>% filter(P.Value < (0.05 / n()))
male_coloc %>%
  filter(PP.H4.abf > 0.9,probe %in% sig_bonf_male$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

male_coloc %>%
  filter(PP.H4.abf < 0.9,probe %in% sig_bonf_male$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

male_coloc %>%
  filter(PP.H4.abf > 0.9,!probe %in% sig_bonf_male$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

Female mQTL

sig_bonf_female <- top_female_prs_hits %>% filter(P.Value < (0.05 / n()))
female_coloc %>%
  filter(PP.H4.abf > 0.9,probe %in% sig_bonf_female$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

female_coloc %>%
  filter(PP.H4.abf < 0.9,probe %in% sig_bonf_female$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

female_coloc %>%
  filter(PP.H4.abf > 0.9,!probe %in% sig_bonf_female$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

Sex stratified GWAS colocalization

load("../prs_analyses/prs_blau_sex_stratified.RData")
male_stratified_coloc <- fread("male_mqtl_male_sumstats_pd_snp_colocalization_ph4.txt.gz")
female_stratified_coloc <- fread("female_mqtl_female_sumstats_pd_snp_colocalization_ph4.txt.gz")

See Script. ### Male results at CpG level

sig_FDR_male <- top_male_ewas %>% filter(adj.P.Val < 0.25)
male_stratified_coloc %>%
  filter(PP.H4.abf > 0.9,probe %in% sig_FDR_male$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name")) %>%
  select(contains("PP.H"),probe,locus,locus_snp,UCSC_RefGene_Name)

male_stratified_coloc %>%
  filter(PP.H4.abf < 0.9,probe %in% sig_FDR_male$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

male_stratified_coloc %>%
  filter(PP.H4.abf > 0.9,!probe %in% sig_FDR_male$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))
NA
NA
sig_FDR_male %>%
  group_by(ID) %>%
  left_join(manifest,by = c("ID"="name")) %>%
  left_join(positions,by= c("ID"="name")) %>%
  select(ID,chr,pos,UCSC_RefGene_Name) %>%
  arrange(chr,pos)
sig_FDR_female <- top_female_ewas %>% filter(adj.P.Val < 0.25)
female_stratified_coloc %>%
  filter(PP.H4.abf > 0.9,probe %in% sig_FDR_female$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

female_stratified_coloc %>%
  filter(PP.H4.abf < 0.9,probe %in% sig_FDR_female$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))

female_stratified_coloc %>%
  filter(PP.H4.abf > 0.9,!probe %in% sig_FDR_female$ID) %>%
  group_by(probe) %>%
  slice(which.max(PP.H4.abf)) %>%
  left_join(manifest,by = c("probe"="name"))
(sig_summary_FDR_female <- sig_FDR_female %>%
  group_by(ID) %>%
  left_join(manifest,by = c("ID"="name")) %>%
  left_join(positions,by= c("ID"="name")) %>%
  select(ID,chr,pos,UCSC_RefGene_Name)%>%
  arrange(as.numeric(gsub("chr","",chr)),pos) )
fdr_female_gostres<- gprofiler2::gost(gsub(";.*","",sig_summary_FDR_female$UCSC_RefGene_Name),organism = "hsapiens",custom_bg = gsub(";.*","",manifest$UCSC_RefGene_Name),correction_method = "fdr",user_threshold = 0.1)
Detected custom background input, domain scope is set to 'custom'
gprofiler2::gostplot(fdr_female_gostres)
`gather_()` was deprecated in tidyr 1.2.0.
Please use `gather()` instead.

male vs female overlap

res <- list("male"=unique(male_results[PP.H4.abf > 0.9]$probe),"female" = unique(female_results[PP.H4.abf > 0.9]$probe))
sex_shared <- Reduce(function(a,b)a[a %in% b],res)
male <- res$male[!res$male %in% res$female]
female <- res$female[!res$female %in% res$male]
plt <- VennDiagram::venn.diagram(res,filename=NULL,cex = 1,
  cat.cex = 1,
  lwd = 2)
grid.newpage()
grid.draw(plt)

Plot overlap

plot_probes_sex <- function(probe_set)
for(cur_probe in probe_set){
  to_plot_male <- male_results_snp[probe == cur_probe]  %>%
    mutate(POS = as.numeric(gsub(".*:","",snp))) %>%
    pivot_longer(c("pvalues.df1","pvalues.df2"),names_to="Var",values_to="Val") %>%
    mutate(Var=recode(Var,pvalues.df1="mQTL",pvalues.df2="GWAS"),Sex="Male")
  to_plot_female <- female_results_snp[probe == cur_probe]  %>%
    mutate(POS = as.numeric(gsub(".*:","",snp))) %>%
    pivot_longer(c("pvalues.df1","pvalues.df2"),names_to="Var",values_to="Val") %>%
    mutate(Var=recode(Var,pvalues.df1="mQTL",pvalues.df2="GWAS"),Sex="Female")
  p<- ggplot(rbind(to_plot_male,to_plot_female),aes(POS,-log10(Val),color=SNP.PP.H4)) +
    geom_point()+
    scale_color_fermenter(palette="Spectral") +
    facet_wrap(Var~Sex)+
    labs(y="-log10 P")+
    ggtitle(cur_probe)
  print(p)
}

Shared

plot_probes_sex(sex_shared)

Male

plot_probes_sex(male)

Female

plot_probes_sex(female)

Mendelian Randomization

library(MendelianRandomization)
library(LDlinkR)
library(glue)
run_mr_per_cpg <- function(mQTL,cur_gwas){
  require(MendelianRandomization)
  require(LDlinkR)
  require(glue)
  mQTL <- mQTL[grepl("rs",SNP)]
  cur_gwas <- cur_gwas[mQTL$SNP, on = "SNP", nomatch=0]
  cur_mQTL <- mQTL[cur_gwas$SNP, on = "SNP", nomatch=0]
  mqtl_rep <- !duplicated(signif(cur_mQTL$beta,1))
  cur_gwas <- cur_gwas[mqtl_rep]
  cur_mQTL <- cur_mQTL[mqtl_rep]
  if(nrow(cur_mQTL) < 1){
    return("No overlapping loci")
  }else if(nrow(cur_mQTL) == 1){
    mr_nocor_input <- mr_input(
      bx = cur_mQTL$beta,
      bxse = cur_mQTL$beta/cur_mQTL$`t-stat` ,
      by = cur_gwas$b,
      byse = cur_gwas$se
    )
    IVWObject <- try(
      mr_ivw(
        mr_nocor_input,
        correl = FALSE,
        distribution = "normal",
        alpha = 0.05
      ),
      silent =  FALSE
    )
  }else{
    attempt <- 1
    cor_table <- NULL
    print(nrow(cur_mQTL))
    while(is.null(cor_table) & attempt < 5){
      try(cor_table <-
        LDmatrix(
          snps=cur_mQTL$SNP,
          pop="EUR",
          genome_build = "grch37",
          token = gsub("\n","",readr::read_file("~/analysis_of_pd_dnam/ldlink_token.txt")),
        ),
        silent = FALSE
      )
      if(is.null(cor_table)){
        Sys.sleep(5)
        attempt <- attempt + 1
      }
    }
    cor_mat <- as.matrix(cor_table[,-c(1)])
    rownames(cor_mat) <- as.character(cor_table$RS_number)
    cur_mQTL <- cur_mQTL[rownames(cor_mat), on="SNP"]
    cur_gwas <- cur_gwas[rownames(cor_mat), on="SNP"]
    if(nrow(cur_mQTL) < 1){
      return("No overlapping loci after corr missing")
    }
    mr_cor_input <- mr_input(
          bx = cur_mQTL$beta,
          bxse = cur_mQTL$beta/cur_mQTL$`t-stat` ,
          by = cur_gwas$b,
          byse = cur_gwas$se,
          correlation = cor_mat
        )
    IVWObject <- try(
      mr_ivw(
        mr_cor_input,
        correl = TRUE,
        distribution = "normal",
        alpha = 0.05
      ),
      silent =  FALSE
    )
  }

  if(class(IVWObject) == 'try-error'){
    return(list(msg=glue("{nrow(cur_mQTL)} mQTL, MR not fit"),obj=mr_cor_input))
  }else{
    return(IVWObject)
  }
}

run MR per colocalized CpG site

cur_GWAS <- fread("~/nalls_PD.QC.gz",key="SNP")
|--------------------------------------------------|
|==================================================|
cross_mQTL <- fread("all_2022_prs_mQTL.txt", key="SNP")
|--------------------------------------------------|
|==================================================|
cur_GWAS <- cur_GWAS[p < 5e-8]
cross_mQTL <- cross_mQTL[`p-value` < 5e-8]
cross_mr_res <- list()
for(probe in unique(cross_coloc[PP.H4.abf > 0.9]$probe)){
  print(probe)
  cross_mr_res[[probe]] <- run_mr_per_cpg(cross_mQTL[gene == probe],cur_GWAS)
}
[1] "cg06632027"
[1] 3

LDlink server is working...
[1] "cg26578617"
[1] 4

LDlink server is working...
[1] "cg03706283"
[1] "cg18586891"
[1] "cg00916973"
[1] "cg01341218"
[1] 3

LDlink server is working...

Error in solve.default(omega) :
  le système est numériquement singulier : conditionnement de la réciproque = 2.63649e-17
[1] "cg03452365"
[1] "cg05159804"
[1] 3

LDlink server is working...
[1] "cg12609785"
[1] 2

LDlink server is working...
[1] "cg13438899"
[1] 3

LDlink server is working...
[1] "cg17117718"
[1] 2

LDlink server is working...
[1] "cg17911788"
[1] 3

LDlink server is working...
[1] "cg19943578"
[1] "cg23659289"
[1] "cg24477401"
[1] 2

LDlink server is working...
[1] "cg12407526"
[1] 3

LDlink server is working...
[1] "cg03867702"
[1] 2

LDlink server is working...
[1] "cg08487618"
[1] "cg10055458"
[1] 2

LDlink server is working...
[1] "cg22659356"
[1] 2

LDlink server is working...
[1] "cg15420606"
[1] "cg17144790"
[1] "cg18816397"
[1] 2

LDlink server is working...
[1] "cg02220965"
[1] "cg03540520"
[1] "cg12441436"
[1] "cg19290994"
[1] "cg27355006"
[1] "cg03392386"
[1] 2

LDlink server is working...
[1] "cg03642635"
[1] "cg12362517"
[1] 2

LDlink server is working...
[1] "cg19961173"
[1] 3

LDlink server is working...
[1] "cg15199181"
[1] "cg19026029"
[1] "cg23670519"
[1] "cg20311846"
[1] "cg24086068"
[1] "cg27417997"
[1] "cg02642017"
[1] "cg09236008"
[1] 4

LDlink server is working...
[1] "cg09755872"
[1] 4

LDlink server is working...
[1] "cg12872830"
[1] 2

LDlink server is working...
[1] "cg14366619"
[1] 3

LDlink server is working...
[1] "cg18081818"
[1] 3

LDlink server is working...
[1] "cg10428661"
[1] "cg19815271"
[1] "cg26099468"
[1] 2

LDlink server is working...

Error in solve.default(omega) :
  le système est numériquement singulier : conditionnement de la réciproque = 4.42528e-17
saveRDS(cross_mr_res,file="cross_mr_res.RData")

Summary Mendelian Randomization

cross_smr <- fread("~/all_2022_dnam_pd.smr")
cross_smr$set <- "Cross-sex GWAS, cross-sex mQTL"
male_nalls_smr <- fread("~/male_nalls_2022_dnam_pd.smr")
male_nalls_smr$set <- "Cross-sex GWAS, male mQTL"
female_nalls_smr <- fread("~/female_nalls_2022_dnam_pd.smr")
female_nalls_smr$set <- "Cross-sex GWAS, female mQTL"
male_blau_smr <- fread("~/male_blauwendraat_2022_dnam_pd.smr")
male_blau_smr$set <- "Male GWAS, male mQTL"
female_blau_smr <- fread("~/female_blauwendraat_2022_dnam_pd.smr")
female_blau_smr$set <- "Female GWAS, female mQTL"
(cross_smr_hits <- cross_smr[p_SMR < (0.05/ .N)])
(cross_smr_hits_heidi <- cross_smr[p_SMR < (0.05/ .N) & p_HEIDI > 0.01])

(male_nalls_smr_hits <- male_nalls_smr[p_SMR < (0.05/ .N)])
(male_nalls_smr_hits_heidi <- male_nalls_smr[p_SMR < (0.05/ .N) & p_HEIDI > 0.01])

(female_nalls_smr_hits <- female_nalls_smr[p_SMR < (0.05/ .N)])
(female_nalls_smr_hits_heidi <- female_nalls_smr[p_SMR < (0.05/ .N) & p_HEIDI > 0.01])

(male_blau_smr_hits <- male_blau_smr[p_SMR < (0.05/ .N)])
(male_blau_smr_hits_heidi <- male_blau_smr[p_SMR < (0.05/ .N) & p_HEIDI > 0.01])

(female_blau_smr_hits <- female_blau_smr[p_SMR < (0.05/ .N)])
(female_blau_smr_hits_heidi <- female_blau_smr[p_SMR < (0.05/ .N) & p_HEIDI > 0.01])
display_venn <- function(x, ...) {
  library(VennDiagram)
  grid.newpage()
  venn_object <- venn.diagram(x, filename = NULL, ...)
  grid.draw(venn_object)
}

display_venn(list(
  cross=cross_smr_hits$probeID,
  male=male_nalls_smr_hits$probeID,
  female=female_nalls_smr_hits$probeID
), fill = c("gray80", "lightblue", "lightpink"))


display_venn(list(
  cross=cross_smr_hits_heidi$probeID,
  male=male_nalls_smr_hits_heidi$probeID,
  female=female_nalls_smr_hits_heidi$probeID
), fill = c("gray80", "lightblue", "lightpink"))


display_venn(list(
  cross=cross_smr_hits$probeID,
  male=male_blau_smr_hits$probeID,
  female=female_blau_smr_hits$probeID
), fill = c("gray80", "lightblue", "lightpink"))


display_venn(list(
  cross=cross_smr_hits_heidi$probeID,
  male=male_blau_smr_hits_heidi$probeID,
  female=female_blau_smr_hits_heidi$probeID
), fill = c("gray80", "lightblue", "lightpink"))



display_venn(list(
  male_nalls=male_nalls_smr_hits_heidi$probeID,
  female_nalls=female_nalls_smr_hits_heidi$probeID,
  male_blau=male_blau_smr_hits$probeID,
  female_blau=female_blau_smr_hits$probeID
), fill = c("lightblue2", "lightpink2","lightblue4","lightpink4"))


display_venn(list(
  male_nalls=male_nalls_smr_hits_heidi$probeID,
  female_nalls=female_nalls_smr_hits_heidi$probeID,
  male_blau=male_blau_smr_hits_heidi$probeID,
  female_blau=female_blau_smr_hits_heidi$probeID
), fill = c("lightblue2", "lightpink2","lightblue4","lightpink4"))

to_plot <- rbind(cross_smr_hits,male_nalls_smr_hits,female_nalls_smr_hits, male_blau_smr_hits,female_blau_smr_hits)
ggplot(to_plot %>% mutate(`HEIDI Test`=ifelse(p_HEIDI > 0.01,"Passed","Failed")) %>%na.omit(),aes(set,fill=`HEIDI Test`))+
  geom_bar() +
  scale_fill_manual(values=c("Passed"="gray20","Failed"="gray70"))+
  geom_text(stat="count",position="stack",mapping=aes(label=..count..),vjust=-0.25)+
  coord_cartesian(ylim=c(0,75)) +
  labs(y="Number of DNAm-mediated\nAssociations",x="")+
  theme_minimal() +
  theme(axis.text.x=element_text(angle=60,hjust=1,vjust=1))

LS0tCnRpdGxlOiAiUG9zdCBtUVRMIGFuYWx5c2lzIFRlcnJlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShjb2xvYykKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KHFxbWFuKQpsaWJyYXJ5KHF2YWx1ZSkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoR3ZpeikKbGlicmFyeSh0aWR5dmVyc2UpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgY2FjaGUgPSBUUlVFKQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFKQpgYGAKIyMgUmVwbGljYXRpb24gb2YgQ3Jvc3Mtc2V4IG1RVEwKYGBge3J9CmxpYnJhcnkoZGJwbHlyKQpsaWJyYXJ5KERCSSkKbnRlc3RzIDwtIDEyNjkwNDA0OQpjb24gPC0gZGJDb25uZWN0KFJTUUxpdGU6OlNRTGl0ZSgpLCAidGVycmVfYW5kX2RpZ3BkX2Npc19hbGxfaW1wdXRlX21RVExfOV9tZXRoeV9QQy5kYiIpCm1RVExfcmVzdWx0cyA8LSB0YmwoY29uLCAidGVycmUiKQpudGVzdHNfZGlncGQgPC0gMTAwMzYzNzQ4Cm1RVExfcmVzdWx0c19kaWdwZCA8LSB0YmwoY29uLCAiZGlncGQiKQpgYGAKYGBge3J9Cm1RVExfcmVzdWx0cyAlPiUKICBmaWx0ZXIoRkRSIDwgMC4wNSkgJT4lCiAgc3VtbWFyaXplKHRvdGFsID0gbigpLCBjcGdzID0gbl9kaXN0aW5jdChnZW5lKSkKbVFUTF9yZXN1bHRzICU+JQogIGZpbHRlcihgcC12YWx1ZWAgPCAoMC4wNSAvIG50ZXN0cykpICU+JQogIHN1bW1hcml6ZSh0b3RhbCA9IG4oKSwgY3BncyA9IG5fZGlzdGluY3QoZ2VuZSkpCgptUVRMX3Jlc3VsdHNfZGlncGQgJT4lCiAgZmlsdGVyKEZEUiA8IDAuMDUpICU+JQogIHN1bW1hcml6ZSh0b3RhbCA9IG4oKSwgY3BncyA9IG5fZGlzdGluY3QoZ2VuZSkpCm1RVExfcmVzdWx0c19kaWdwZCAlPiUKICBmaWx0ZXIoYHAtdmFsdWVgIDwgKDAuMDUgLyBudGVzdHMpKSAlPiUKICBzdW1tYXJpemUodG90YWwgPSBuKCksIGNwZ3MgPSBuX2Rpc3RpbmN0KGdlbmUpKQoKbGlicmFyeShkYnBsb3QpCm1RVExfcmVzdWx0cyAlPiUgZGJwbG90X2hpc3RvZ3JhbShgcC12YWx1ZWApCiMgcXEobVFUTF9yZXN1bHRzJGBwLXZhbHVlYCkKYGBgCgojIyMgQ2hlY2tpbmcgcmVwbGljYXRpb24KJFxwaV8xJCBzdGF0aXN0aWNzCmBgYHtyfQp0b19jaGVja19yZXBsaWNhdGlvbiA8LSBtUVRMX3Jlc3VsdHNfZGlncGQgJT4lCiAgaW5uZXJfam9pbihtUVRMX3Jlc3VsdHMgJT4lIGZpbHRlcihGRFIgPCAwLjA1KSwgYnkgPSBjKCJTTlAiLCAiZ2VuZSIpLCBjb3B5ID0gVFJVRSkgJT4lCiAgY29sbGVjdCgpICU+JSBhcy5kYXRhLnRhYmxlKCkKCnBpMHJlcyA8LSBwaTBlc3QobmEub21pdChhcy5udW1lcmljKHRvX2NoZWNrX3JlcGxpY2F0aW9uJGBwLXZhbHVlLnhgKSksIGxhbWJkYSA9IHNlcSgwLCAwLjI0NSwgMC4wMDUpLCBwaTAubWV0aG9kID0gImJvb3RzdHJhcCIpCjEgLSBwaTByZXMkcGkwCnBsb3QocGkwcmVzJHBpMC5sYW1iZGEsIHBpMHJlcyRsYW1iZGEsIHhsYWIgPSBicXVvdGUocGlbMF0pLCB5bGFiID0gYnF1b3RlKGxhbWJkYSkpCmFibGluZSh2ID0gcGkwcmVzJHBpMCkKcXZhbHJlcyA8LSBxdmFsdWVfdHJ1bmNwKG5hLm9taXQoYXMubnVtZXJpYyh0b19jaGVja19yZXBsaWNhdGlvbiRgcC12YWx1ZS54YCkpKQoxIC0gcXZhbHJlcyRwaTAKYWJsaW5lKHYgPSBxdmFscmVzJHBpMCkKcGkwZXN0KGFzLm51bWVyaWModG9fY2hlY2tfcmVwbGljYXRpb24kYHAtdmFsdWUueGApIC8gMC4yNSkKYGBgCgojIENvbG9jYWxpemF0aW9uIHdpdGggcGFya2luc29uJ3MgbG9jaSBhbmQgb3ZlcmxhcCB3aXRoIGV3YXMgaGl0cwpgYGB7cn0KbG9hZCgiLi4vcHJzX2FuYWx5c2VzL3Byc19uYWxsc19jcm9zc193X3NleF9zdHJhdGlmaWVkLlJEYXRhIikKbWFuaWZlc3QgPC0gSWxsdW1pbmFIdW1hbk1ldGh5bGF0aW9uRVBJQ2Fubm8uaWxtMTBiNC5oZzE5OjpPdGhlciAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJuYW1lIikKCnBvc2l0aW9ucyA8LSBJbGx1bWluYUh1bWFuTWV0aHlsYXRpb25FUElDYW5uby5pbG0xMGI0LmhnMTk6OkxvY2F0aW9ucyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJuYW1lIikKYGBgCgojIyBjcm9zcy1zZXggR1dBUwpgYGB7cn0KY3Jvc3NfY29sb2MgPC0gZnJlYWQoImNyb3NzX21xdGxfY3Jvc3Nfc3Vtc3RhdHNfcGRfc25wX2NvbG9jYWxpemF0aW9uX3BoNC50eHQuZ3oiKQptYWxlX2NvbG9jIDwtIGZyZWFkKCJtYWxlX21xdGxfY3Jvc3Nfc3Vtc3RhdHNfcGRfc25wX2NvbG9jYWxpemF0aW9uX3BoNC50eHQuZ3oiKQpmZW1hbGVfY29sb2MgPC0gZnJlYWQoImZlbWFsZV9tcXRsX2Nyb3NzX3N1bXN0YXRzX3BkX3NucF9jb2xvY2FsaXphdGlvbl9waDQudHh0Lmd6IikKYGBgCiMjIyBDcm9zcy1zZXggbVFUTApgYGB7cn0Kc2lnX2JvbmZfY3Jvc3MgPC0gdG9wX3Byc19oaXRzICU+JSBmaWx0ZXIocC5hZGp1c3QoUC5WYWx1ZSxtZXRob2Q9IkJIIikgPCAwLjA1KQpjcm9zc19jb2xvYyAlPiUKICBmaWx0ZXIoUFAuSDQuYWJmID4gMC45LHByb2JlICVpbiUgc2lnX2JvbmZfY3Jvc3MkSUQpICU+JQogIGdyb3VwX2J5KHByb2JlKSAlPiUKICBzbGljZSh3aGljaC5tYXgoUFAuSDQuYWJmKSkgJT4lCiAgbGVmdF9qb2luKG1hbmlmZXN0LGJ5ID0gYygicHJvYmUiPSJuYW1lIikpCgpjcm9zc19jb2xvY19wcm9iZXMgPC0gY3Jvc3NfY29sb2MgJT4lCiAgZmlsdGVyKFBQLkg0LmFiZiA+IDAuOSxwcm9iZSAlaW4lIHNpZ19ib25mX2Nyb3NzJElEKSAlPiUKICBncm91cF9ieShwcm9iZSkgJT4lCiAgc2xpY2Uod2hpY2gubWF4KFBQLkg0LmFiZikpICU+JQogIGxlZnRfam9pbihtYW5pZmVzdCxieSA9IGMoInByb2JlIj0ibmFtZSIpKSAlPiUgc2VsZWN0KHByb2JlKQoKY3Jvc3NfY29sb2MgJT4lCiAgZmlsdGVyKFBQLkg0LmFiZiA8IDAuOSxwcm9iZSAlaW4lIHNpZ19ib25mX2Nyb3NzJElEKSAlPiUKICBncm91cF9ieShwcm9iZSkgJT4lCiAgc2xpY2Uod2hpY2gubWF4KFBQLkg0LmFiZikpICU+JQogIGxlZnRfam9pbihtYW5pZmVzdCxieSA9IGMoInByb2JlIj0ibmFtZSIpKQoKY3Jvc3NfY29sb2MgJT4lCiAgZmlsdGVyKFBQLkg0LmFiZiA+IDAuOSwhcHJvYmUgJWluJSBzaWdfYm9uZl9jcm9zcyRJRCkgJT4lCiAgZ3JvdXBfYnkocHJvYmUpICU+JQogIHNsaWNlKHdoaWNoLm1heChQUC5INC5hYmYpKSAlPiUKICBsZWZ0X2pvaW4obWFuaWZlc3QsYnkgPSBjKCJwcm9iZSI9Im5hbWUiKSkKYGBgCmBgYHtyfQoobm9uX2NvbG9jYWxpemVkX2Nyb3NzIDwtIHNpZ19ib25mX2Nyb3NzICU+JSBsZWZ0X2pvaW4obWFuaWZlc3QsYnk9YygiSUQiID0gIm5hbWUiKSkgICU+JSBsZWZ0X2pvaW4ocG9zaXRpb25zLGJ5PWMoIklEIj0ibmFtZSIpKSAlPiUgZmlsdGVyKCFJRCAlaW4lIGNyb3NzX2NvbG9jX3Byb2JlcyRwcm9iZSkgJT4lIGFycmFuZ2UoY2hyLHBvcykgICU+JSBzZWxlY3QoSUQsVUNTQ19SZWZHZW5lX05hbWUsY2hyLHBvcykpCgpub25fY29sb2NhbGl6ZWRfY3Jvc3MgJT4lIGNvdW50KGdzdWIoIjsuKiIsIiIsVUNTQ19SZWZHZW5lX05hbWUpKQpgYGAKCgojIyMgTWFsZSBtUVRMCmBgYHtyfQpzaWdfYm9uZl9tYWxlIDwtIHRvcF9tYWxlX3Byc19oaXRzICU+JSBmaWx0ZXIoUC5WYWx1ZSA8ICgwLjA1IC8gbigpKSkKbWFsZV9jb2xvYyAlPiUKICBmaWx0ZXIoUFAuSDQuYWJmID4gMC45LHByb2JlICVpbiUgc2lnX2JvbmZfbWFsZSRJRCkgJT4lCiAgZ3JvdXBfYnkocHJvYmUpICU+JQogIHNsaWNlKHdoaWNoLm1heChQUC5INC5hYmYpKSAlPiUKICBsZWZ0X2pvaW4obWFuaWZlc3QsYnkgPSBjKCJwcm9iZSI9Im5hbWUiKSkKCm1hbGVfY29sb2MgJT4lCiAgZmlsdGVyKFBQLkg0LmFiZiA8IDAuOSxwcm9iZSAlaW4lIHNpZ19ib25mX21hbGUkSUQpICU+JQogIGdyb3VwX2J5KHByb2JlKSAlPiUKICBzbGljZSh3aGljaC5tYXgoUFAuSDQuYWJmKSkgJT4lCiAgbGVmdF9qb2luKG1hbmlmZXN0LGJ5ID0gYygicHJvYmUiPSJuYW1lIikpCgptYWxlX2NvbG9jICU+JQogIGZpbHRlcihQUC5INC5hYmYgPiAwLjksIXByb2JlICVpbiUgc2lnX2JvbmZfbWFsZSRJRCkgJT4lCiAgZ3JvdXBfYnkocHJvYmUpICU+JQogIHNsaWNlKHdoaWNoLm1heChQUC5INC5hYmYpKSAlPiUKICBsZWZ0X2pvaW4obWFuaWZlc3QsYnkgPSBjKCJwcm9iZSI9Im5hbWUiKSkKYGBgCgojIyMgRmVtYWxlIG1RVEwKYGBge3J9CnNpZ19ib25mX2ZlbWFsZSA8LSB0b3BfZmVtYWxlX3Byc19oaXRzICU+JSBmaWx0ZXIoUC5WYWx1ZSA8ICgwLjA1IC8gbigpKSkKZmVtYWxlX2NvbG9jICU+JQogIGZpbHRlcihQUC5INC5hYmYgPiAwLjkscHJvYmUgJWluJSBzaWdfYm9uZl9mZW1hbGUkSUQpICU+JQogIGdyb3VwX2J5KHByb2JlKSAlPiUKICBzbGljZSh3aGljaC5tYXgoUFAuSDQuYWJmKSkgJT4lCiAgbGVmdF9qb2luKG1hbmlmZXN0LGJ5ID0gYygicHJvYmUiPSJuYW1lIikpCgpmZW1hbGVfY29sb2MgJT4lCiAgZmlsdGVyKFBQLkg0LmFiZiA8IDAuOSxwcm9iZSAlaW4lIHNpZ19ib25mX2ZlbWFsZSRJRCkgJT4lCiAgZ3JvdXBfYnkocHJvYmUpICU+JQogIHNsaWNlKHdoaWNoLm1heChQUC5INC5hYmYpKSAlPiUKICBsZWZ0X2pvaW4obWFuaWZlc3QsYnkgPSBjKCJwcm9iZSI9Im5hbWUiKSkKCmZlbWFsZV9jb2xvYyAlPiUKICBmaWx0ZXIoUFAuSDQuYWJmID4gMC45LCFwcm9iZSAlaW4lIHNpZ19ib25mX2ZlbWFsZSRJRCkgJT4lCiAgZ3JvdXBfYnkocHJvYmUpICU+JQogIHNsaWNlKHdoaWNoLm1heChQUC5INC5hYmYpKSAlPiUKICBsZWZ0X2pvaW4obWFuaWZlc3QsYnkgPSBjKCJwcm9iZSI9Im5hbWUiKSkKYGBgCgojIyBTZXggc3RyYXRpZmllZCBHV0FTIGNvbG9jYWxpemF0aW9uCmBgYHtyfQpsb2FkKCIuLi9wcnNfYW5hbHlzZXMvcHJzX2JsYXVfc2V4X3N0cmF0aWZpZWQuUkRhdGEiKQptYWxlX3N0cmF0aWZpZWRfY29sb2MgPC0gZnJlYWQoIm1hbGVfbXF0bF9tYWxlX3N1bXN0YXRzX3BkX3NucF9jb2xvY2FsaXphdGlvbl9waDQudHh0Lmd6IikKZmVtYWxlX3N0cmF0aWZpZWRfY29sb2MgPC0gZnJlYWQoImZlbWFsZV9tcXRsX2ZlbWFsZV9zdW1zdGF0c19wZF9zbnBfY29sb2NhbGl6YXRpb25fcGg0LnR4dC5neiIpCmBgYAoKU2VlIFtTY3JpcHRdKHJ1bl9jb2xvY2FsaXphdGlvbi5SKS4KIyMjIE1hbGUgcmVzdWx0cyBhdCBDcEcgbGV2ZWwKYGBge3J9CnNpZ19GRFJfbWFsZSA8LSB0b3BfbWFsZV9ld2FzICU+JSBmaWx0ZXIoYWRqLlAuVmFsIDwgMC4yNSkKbWFsZV9zdHJhdGlmaWVkX2NvbG9jICU+JQogIGZpbHRlcihQUC5INC5hYmYgPiAwLjkscHJvYmUgJWluJSBzaWdfRkRSX21hbGUkSUQpICU+JQogIGdyb3VwX2J5KHByb2JlKSAlPiUKICBzbGljZSh3aGljaC5tYXgoUFAuSDQuYWJmKSkgJT4lCiAgbGVmdF9qb2luKG1hbmlmZXN0LGJ5ID0gYygicHJvYmUiPSJuYW1lIikpICU+JQogIHNlbGVjdChjb250YWlucygiUFAuSCIpLHByb2JlLGxvY3VzLGxvY3VzX3NucCxVQ1NDX1JlZkdlbmVfTmFtZSkKCm1hbGVfc3RyYXRpZmllZF9jb2xvYyAlPiUKICBmaWx0ZXIoUFAuSDQuYWJmIDwgMC45LHByb2JlICVpbiUgc2lnX0ZEUl9tYWxlJElEKSAlPiUKICBncm91cF9ieShwcm9iZSkgJT4lCiAgc2xpY2Uod2hpY2gubWF4KFBQLkg0LmFiZikpICU+JQogIGxlZnRfam9pbihtYW5pZmVzdCxieSA9IGMoInByb2JlIj0ibmFtZSIpKQoKbWFsZV9zdHJhdGlmaWVkX2NvbG9jICU+JQogIGZpbHRlcihQUC5INC5hYmYgPiAwLjksIXByb2JlICVpbiUgc2lnX0ZEUl9tYWxlJElEKSAlPiUKICBncm91cF9ieShwcm9iZSkgJT4lCiAgc2xpY2Uod2hpY2gubWF4KFBQLkg0LmFiZikpICU+JQogIGxlZnRfam9pbihtYW5pZmVzdCxieSA9IGMoInByb2JlIj0ibmFtZSIpKQoKCmBgYApgYGB7cn0Kc2lnX0ZEUl9tYWxlICU+JQogIGdyb3VwX2J5KElEKSAlPiUKICBsZWZ0X2pvaW4obWFuaWZlc3QsYnkgPSBjKCJJRCI9Im5hbWUiKSkgJT4lIAogIGxlZnRfam9pbihwb3NpdGlvbnMsYnk9IGMoIklEIj0ibmFtZSIpKSAlPiUgCiAgc2VsZWN0KElELGNocixwb3MsVUNTQ19SZWZHZW5lX05hbWUpICU+JSAKICBhcnJhbmdlKGNocixwb3MpCmBgYAoKYGBge3J9CnNpZ19GRFJfZmVtYWxlIDwtIHRvcF9mZW1hbGVfZXdhcyAlPiUgZmlsdGVyKGFkai5QLlZhbCA8IDAuMjUpCmZlbWFsZV9zdHJhdGlmaWVkX2NvbG9jICU+JQogIGZpbHRlcihQUC5INC5hYmYgPiAwLjkscHJvYmUgJWluJSBzaWdfRkRSX2ZlbWFsZSRJRCkgJT4lCiAgZ3JvdXBfYnkocHJvYmUpICU+JQogIHNsaWNlKHdoaWNoLm1heChQUC5INC5hYmYpKSAlPiUKICBsZWZ0X2pvaW4obWFuaWZlc3QsYnkgPSBjKCJwcm9iZSI9Im5hbWUiKSkKCmZlbWFsZV9zdHJhdGlmaWVkX2NvbG9jICU+JQogIGZpbHRlcihQUC5INC5hYmYgPCAwLjkscHJvYmUgJWluJSBzaWdfRkRSX2ZlbWFsZSRJRCkgJT4lCiAgZ3JvdXBfYnkocHJvYmUpICU+JQogIHNsaWNlKHdoaWNoLm1heChQUC5INC5hYmYpKSAlPiUKICBsZWZ0X2pvaW4obWFuaWZlc3QsYnkgPSBjKCJwcm9iZSI9Im5hbWUiKSkKCmZlbWFsZV9zdHJhdGlmaWVkX2NvbG9jICU+JQogIGZpbHRlcihQUC5INC5hYmYgPiAwLjksIXByb2JlICVpbiUgc2lnX0ZEUl9mZW1hbGUkSUQpICU+JQogIGdyb3VwX2J5KHByb2JlKSAlPiUKICBzbGljZSh3aGljaC5tYXgoUFAuSDQuYWJmKSkgJT4lCiAgbGVmdF9qb2luKG1hbmlmZXN0LGJ5ID0gYygicHJvYmUiPSJuYW1lIikpCmBgYApgYGB7cn0KKHNpZ19zdW1tYXJ5X0ZEUl9mZW1hbGUgPC0gc2lnX0ZEUl9mZW1hbGUgJT4lCiAgZ3JvdXBfYnkoSUQpICU+JQogIGxlZnRfam9pbihtYW5pZmVzdCxieSA9IGMoIklEIj0ibmFtZSIpKSAlPiUgCiAgbGVmdF9qb2luKHBvc2l0aW9ucyxieT0gYygiSUQiPSJuYW1lIikpICU+JSAKICBzZWxlY3QoSUQsY2hyLHBvcyxVQ1NDX1JlZkdlbmVfTmFtZSklPiUKICBhcnJhbmdlKGFzLm51bWVyaWMoZ3N1YigiY2hyIiwiIixjaHIpKSxwb3MpICkKYGBgCmBgYHtyfQpmZHJfZmVtYWxlX2dvc3RyZXM8LSBncHJvZmlsZXIyOjpnb3N0KGdzdWIoIjsuKiIsIiIsc2lnX3N1bW1hcnlfRkRSX2ZlbWFsZSRVQ1NDX1JlZkdlbmVfTmFtZSksb3JnYW5pc20gPSAiaHNhcGllbnMiLGN1c3RvbV9iZyA9IGdzdWIoIjsuKiIsIiIsbWFuaWZlc3QkVUNTQ19SZWZHZW5lX05hbWUpLGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsdXNlcl90aHJlc2hvbGQgPSAwLjEpCmdwcm9maWxlcjI6Omdvc3RwbG90KGZkcl9mZW1hbGVfZ29zdHJlcykKYGBgCgojIyMgbWFsZSB2cyBmZW1hbGUgb3ZlcmxhcAoKYGBge3J9CnJlcyA8LSBsaXN0KCJtYWxlIj11bmlxdWUobWFsZV9yZXN1bHRzW1BQLkg0LmFiZiA+IDAuOV0kcHJvYmUpLCJmZW1hbGUiID0gdW5pcXVlKGZlbWFsZV9yZXN1bHRzW1BQLkg0LmFiZiA+IDAuOV0kcHJvYmUpKQpzZXhfc2hhcmVkIDwtIFJlZHVjZShmdW5jdGlvbihhLGIpYVthICVpbiUgYl0scmVzKQptYWxlIDwtIHJlcyRtYWxlWyFyZXMkbWFsZSAlaW4lIHJlcyRmZW1hbGVdCmZlbWFsZSA8LSByZXMkZmVtYWxlWyFyZXMkZmVtYWxlICVpbiUgcmVzJG1hbGVdCnBsdCA8LSBWZW5uRGlhZ3JhbTo6dmVubi5kaWFncmFtKHJlcyxmaWxlbmFtZT1OVUxMLGNleCA9IDEsCiAgY2F0LmNleCA9IDEsCiAgbHdkID0gMikKZ3JpZC5uZXdwYWdlKCkKZ3JpZC5kcmF3KHBsdCkKYGBgCiMjIFBsb3Qgb3ZlcmxhcApgYGB7cn0KcGxvdF9wcm9iZXNfc2V4IDwtIGZ1bmN0aW9uKHByb2JlX3NldCkKZm9yKGN1cl9wcm9iZSBpbiBwcm9iZV9zZXQpewogIHRvX3Bsb3RfbWFsZSA8LSBtYWxlX3Jlc3VsdHNfc25wW3Byb2JlID09IGN1cl9wcm9iZV0gICU+JQogICAgbXV0YXRlKFBPUyA9IGFzLm51bWVyaWMoZ3N1YigiLio6IiwiIixzbnApKSkgJT4lCiAgICBwaXZvdF9sb25nZXIoYygicHZhbHVlcy5kZjEiLCJwdmFsdWVzLmRmMiIpLG5hbWVzX3RvPSJWYXIiLHZhbHVlc190bz0iVmFsIikgJT4lCiAgICBtdXRhdGUoVmFyPXJlY29kZShWYXIscHZhbHVlcy5kZjE9Im1RVEwiLHB2YWx1ZXMuZGYyPSJHV0FTIiksU2V4PSJNYWxlIikKICB0b19wbG90X2ZlbWFsZSA8LSBmZW1hbGVfcmVzdWx0c19zbnBbcHJvYmUgPT0gY3VyX3Byb2JlXSAgJT4lCiAgICBtdXRhdGUoUE9TID0gYXMubnVtZXJpYyhnc3ViKCIuKjoiLCIiLHNucCkpKSAlPiUKICAgIHBpdm90X2xvbmdlcihjKCJwdmFsdWVzLmRmMSIsInB2YWx1ZXMuZGYyIiksbmFtZXNfdG89IlZhciIsdmFsdWVzX3RvPSJWYWwiKSAlPiUKICAgIG11dGF0ZShWYXI9cmVjb2RlKFZhcixwdmFsdWVzLmRmMT0ibVFUTCIscHZhbHVlcy5kZjI9IkdXQVMiKSxTZXg9IkZlbWFsZSIpCiAgcDwtIGdncGxvdChyYmluZCh0b19wbG90X21hbGUsdG9fcGxvdF9mZW1hbGUpLGFlcyhQT1MsLWxvZzEwKFZhbCksY29sb3I9U05QLlBQLkg0KSkgKwogICAgZ2VvbV9wb2ludCgpKwogICAgc2NhbGVfY29sb3JfZmVybWVudGVyKHBhbGV0dGU9IlNwZWN0cmFsIikgKwogICAgZmFjZXRfd3JhcChWYXJ+U2V4KSsKICAgIGxhYnMoeT0iLWxvZzEwIFAiKSsKICAgIGdndGl0bGUoY3VyX3Byb2JlKQogIHByaW50KHApCn0KYGBgCiMjIyBTaGFyZWQKYGBge3J9CnBsb3RfcHJvYmVzX3NleChzZXhfc2hhcmVkKQpgYGAKCiMjIyBNYWxlCmBgYHtyfQpwbG90X3Byb2Jlc19zZXgobWFsZSkKYGBgCiMjIyBGZW1hbGUKYGBge3J9CnBsb3RfcHJvYmVzX3NleChmZW1hbGUpCmBgYAojIyBNZW5kZWxpYW4gUmFuZG9taXphdGlvbgpgYGB7cn0KbGlicmFyeShNZW5kZWxpYW5SYW5kb21pemF0aW9uKQpsaWJyYXJ5KExEbGlua1IpCmxpYnJhcnkoZ2x1ZSkKcnVuX21yX3Blcl9jcGcgPC0gZnVuY3Rpb24obVFUTCxjdXJfZ3dhcyl7CiAgcmVxdWlyZShNZW5kZWxpYW5SYW5kb21pemF0aW9uKQogIHJlcXVpcmUoTERsaW5rUikKICByZXF1aXJlKGdsdWUpCiAgbVFUTCA8LSBtUVRMW2dyZXBsKCJycyIsU05QKV0KICBjdXJfZ3dhcyA8LSBjdXJfZ3dhc1ttUVRMJFNOUCwgb24gPSAiU05QIiwgbm9tYXRjaD0wXQogIGN1cl9tUVRMIDwtIG1RVExbY3VyX2d3YXMkU05QLCBvbiA9ICJTTlAiLCBub21hdGNoPTBdCiAgbXF0bF9yZXAgPC0gIWR1cGxpY2F0ZWQoc2lnbmlmKGN1cl9tUVRMJGJldGEsMSkpCiAgY3VyX2d3YXMgPC0gY3VyX2d3YXNbbXF0bF9yZXBdCiAgY3VyX21RVEwgPC0gY3VyX21RVExbbXF0bF9yZXBdCiAgaWYobnJvdyhjdXJfbVFUTCkgPCAxKXsKICAgIHJldHVybigiTm8gb3ZlcmxhcHBpbmcgbG9jaSIpCiAgfWVsc2UgaWYobnJvdyhjdXJfbVFUTCkgPT0gMSl7CiAgICBtcl9ub2Nvcl9pbnB1dCA8LSBtcl9pbnB1dCgKICAgICAgYnggPSBjdXJfbVFUTCRiZXRhLAogICAgICBieHNlID0gY3VyX21RVEwkYmV0YS9jdXJfbVFUTCRgdC1zdGF0YCAsCiAgICAgIGJ5ID0gY3VyX2d3YXMkYiwKICAgICAgYnlzZSA9IGN1cl9nd2FzJHNlCiAgICApCiAgICBJVldPYmplY3QgPC0gdHJ5KAogICAgICBtcl9pdncoCiAgICAgICAgbXJfbm9jb3JfaW5wdXQsCiAgICAgICAgY29ycmVsID0gRkFMU0UsCiAgICAgICAgZGlzdHJpYnV0aW9uID0gIm5vcm1hbCIsCiAgICAgICAgYWxwaGEgPSAwLjA1CiAgICAgICksCiAgICAgIHNpbGVudCA9ICBGQUxTRQogICAgKQogIH1lbHNlewogICAgYXR0ZW1wdCA8LSAxCiAgICBjb3JfdGFibGUgPC0gTlVMTAogICAgcHJpbnQobnJvdyhjdXJfbVFUTCkpCiAgICB3aGlsZShpcy5udWxsKGNvcl90YWJsZSkgJiBhdHRlbXB0IDwgNSl7CiAgICAgIHRyeShjb3JfdGFibGUgPC0KICAgICAgICBMRG1hdHJpeCgKICAgICAgICAgIHNucHM9Y3VyX21RVEwkU05QLAogICAgICAgICAgcG9wPSJFVVIiLAogICAgICAgICAgZ2Vub21lX2J1aWxkID0gImdyY2gzNyIsCiAgICAgICAgICB0b2tlbiA9IGdzdWIoIlxuIiwiIixyZWFkcjo6cmVhZF9maWxlKCJ+L2FuYWx5c2lzX29mX3BkX2RuYW0vbGRsaW5rX3Rva2VuLnR4dCIpKSwKICAgICAgICApLAogICAgICAgIHNpbGVudCA9IEZBTFNFCiAgICAgICkKICAgICAgaWYoaXMubnVsbChjb3JfdGFibGUpKXsKICAgICAgICBTeXMuc2xlZXAoNSkKICAgICAgICBhdHRlbXB0IDwtIGF0dGVtcHQgKyAxCiAgICAgIH0KICAgIH0KICAgIGNvcl9tYXQgPC0gYXMubWF0cml4KGNvcl90YWJsZVssLWMoMSldKQogICAgcm93bmFtZXMoY29yX21hdCkgPC0gYXMuY2hhcmFjdGVyKGNvcl90YWJsZSRSU19udW1iZXIpCiAgICBjdXJfbVFUTCA8LSBjdXJfbVFUTFtyb3duYW1lcyhjb3JfbWF0KSwgb249IlNOUCJdCiAgICBjdXJfZ3dhcyA8LSBjdXJfZ3dhc1tyb3duYW1lcyhjb3JfbWF0KSwgb249IlNOUCJdCiAgICBpZihucm93KGN1cl9tUVRMKSA8IDEpewogICAgICByZXR1cm4oIk5vIG92ZXJsYXBwaW5nIGxvY2kgYWZ0ZXIgY29yciBtaXNzaW5nIikKICAgIH0KICAgIG1yX2Nvcl9pbnB1dCA8LSBtcl9pbnB1dCgKICAgICAgICAgIGJ4ID0gY3VyX21RVEwkYmV0YSwKICAgICAgICAgIGJ4c2UgPSBjdXJfbVFUTCRiZXRhL2N1cl9tUVRMJGB0LXN0YXRgICwKICAgICAgICAgIGJ5ID0gY3VyX2d3YXMkYiwKICAgICAgICAgIGJ5c2UgPSBjdXJfZ3dhcyRzZSwKICAgICAgICAgIGNvcnJlbGF0aW9uID0gY29yX21hdAogICAgICAgICkKICAgIElWV09iamVjdCA8LSB0cnkoCiAgICAgIG1yX2l2dygKICAgICAgICBtcl9jb3JfaW5wdXQsCiAgICAgICAgY29ycmVsID0gVFJVRSwKICAgICAgICBkaXN0cmlidXRpb24gPSAibm9ybWFsIiwKICAgICAgICBhbHBoYSA9IDAuMDUKICAgICAgKSwKICAgICAgc2lsZW50ID0gIEZBTFNFCiAgICApCiAgfQoKICBpZihjbGFzcyhJVldPYmplY3QpID09ICd0cnktZXJyb3InKXsKICAgIHJldHVybihsaXN0KG1zZz1nbHVlKCJ7bnJvdyhjdXJfbVFUTCl9IG1RVEwsIE1SIG5vdCBmaXQiKSxvYmo9bXJfY29yX2lucHV0KSkKICB9ZWxzZXsKICAgIHJldHVybihJVldPYmplY3QpCiAgfQp9CmBgYAoKIyMjIyBydW4gTVIgcGVyIGNvbG9jYWxpemVkIENwRyBzaXRlCmBgYHtyfQpjdXJfR1dBUyA8LSBmcmVhZCgifi9uYWxsc19QRC5RQy5neiIsa2V5PSJTTlAiKQpjcm9zc19tUVRMIDwtIGZyZWFkKCJhbGxfMjAyMl9wcnNfbVFUTC50eHQiLCBrZXk9IlNOUCIpCmBgYApgYGB7cn0KY3VyX0dXQVMgPC0gY3VyX0dXQVNbcCA8IDVlLThdCmNyb3NzX21RVEwgPC0gY3Jvc3NfbVFUTFtgcC12YWx1ZWAgPCA1ZS04XQpjcm9zc19tcl9yZXMgPC0gbGlzdCgpCmZvcihwcm9iZSBpbiB1bmlxdWUoY3Jvc3NfY29sb2NbUFAuSDQuYWJmID4gMC45XSRwcm9iZSkpewogIHByaW50KHByb2JlKQogIGNyb3NzX21yX3Jlc1tbcHJvYmVdXSA8LSBydW5fbXJfcGVyX2NwZyhjcm9zc19tUVRMW2dlbmUgPT0gcHJvYmVdLGN1cl9HV0FTKQp9CmBgYApgYGB7cn0Kc2F2ZVJEUyhjcm9zc19tcl9yZXMsZmlsZT0iY3Jvc3NfbXJfcmVzLlJEYXRhIikKYGBgCiMjIFN1bW1hcnkgTWVuZGVsaWFuIFJhbmRvbWl6YXRpb24KYGBge3J9CmNyb3NzX3NtciA8LSBmcmVhZCgifi9hbGxfMjAyMl9kbmFtX3BkLnNtciIpCmNyb3NzX3NtciRzZXQgPC0gIkNyb3NzLXNleCBHV0FTLCBjcm9zcy1zZXggbVFUTCIKbWFsZV9uYWxsc19zbXIgPC0gZnJlYWQoIn4vbWFsZV9uYWxsc18yMDIyX2RuYW1fcGQuc21yIikKbWFsZV9uYWxsc19zbXIkc2V0IDwtICJDcm9zcy1zZXggR1dBUywgbWFsZSBtUVRMIgpmZW1hbGVfbmFsbHNfc21yIDwtIGZyZWFkKCJ+L2ZlbWFsZV9uYWxsc18yMDIyX2RuYW1fcGQuc21yIikKZmVtYWxlX25hbGxzX3NtciRzZXQgPC0gIkNyb3NzLXNleCBHV0FTLCBmZW1hbGUgbVFUTCIKbWFsZV9ibGF1X3NtciA8LSBmcmVhZCgifi9tYWxlX2JsYXV3ZW5kcmFhdF8yMDIyX2RuYW1fcGQuc21yIikKbWFsZV9ibGF1X3NtciRzZXQgPC0gIk1hbGUgR1dBUywgbWFsZSBtUVRMIgpmZW1hbGVfYmxhdV9zbXIgPC0gZnJlYWQoIn4vZmVtYWxlX2JsYXV3ZW5kcmFhdF8yMDIyX2RuYW1fcGQuc21yIikKZmVtYWxlX2JsYXVfc21yJHNldCA8LSAiRmVtYWxlIEdXQVMsIGZlbWFsZSBtUVRMIgpgYGAKCmBgYHtyfQooY3Jvc3Nfc21yX2hpdHMgPC0gY3Jvc3Nfc21yW3BfU01SIDwgKDAuMDUvIC5OKV0pCihjcm9zc19zbXJfaGl0c19oZWlkaSA8LSBjcm9zc19zbXJbcF9TTVIgPCAoMC4wNS8gLk4pICYgcF9IRUlESSA+IDAuMDFdKQoKKG1hbGVfbmFsbHNfc21yX2hpdHMgPC0gbWFsZV9uYWxsc19zbXJbcF9TTVIgPCAoMC4wNS8gLk4pXSkKKG1hbGVfbmFsbHNfc21yX2hpdHNfaGVpZGkgPC0gbWFsZV9uYWxsc19zbXJbcF9TTVIgPCAoMC4wNS8gLk4pICYgcF9IRUlESSA+IDAuMDFdKQoKKGZlbWFsZV9uYWxsc19zbXJfaGl0cyA8LSBmZW1hbGVfbmFsbHNfc21yW3BfU01SIDwgKDAuMDUvIC5OKV0pCihmZW1hbGVfbmFsbHNfc21yX2hpdHNfaGVpZGkgPC0gZmVtYWxlX25hbGxzX3NtcltwX1NNUiA8ICgwLjA1LyAuTikgJiBwX0hFSURJID4gMC4wMV0pCgoobWFsZV9ibGF1X3Ntcl9oaXRzIDwtIG1hbGVfYmxhdV9zbXJbcF9TTVIgPCAoMC4wNS8gLk4pXSkKKG1hbGVfYmxhdV9zbXJfaGl0c19oZWlkaSA8LSBtYWxlX2JsYXVfc21yW3BfU01SIDwgKDAuMDUvIC5OKSAmIHBfSEVJREkgPiAwLjAxXSkKCihmZW1hbGVfYmxhdV9zbXJfaGl0cyA8LSBmZW1hbGVfYmxhdV9zbXJbcF9TTVIgPCAoMC4wNS8gLk4pXSkKKGZlbWFsZV9ibGF1X3Ntcl9oaXRzX2hlaWRpIDwtIGZlbWFsZV9ibGF1X3NtcltwX1NNUiA8ICgwLjA1LyAuTikgJiBwX0hFSURJID4gMC4wMV0pCmBgYAoKYGBge3J9CmRpc3BsYXlfdmVubiA8LSBmdW5jdGlvbih4LCAuLi4pIHsKICBsaWJyYXJ5KFZlbm5EaWFncmFtKQogIGdyaWQubmV3cGFnZSgpCiAgdmVubl9vYmplY3QgPC0gdmVubi5kaWFncmFtKHgsIGZpbGVuYW1lID0gTlVMTCwgLi4uKQogIGdyaWQuZHJhdyh2ZW5uX29iamVjdCkKfQoKZGlzcGxheV92ZW5uKGxpc3QoCiAgY3Jvc3M9Y3Jvc3Nfc21yX2hpdHMkcHJvYmVJRCwKICBtYWxlPW1hbGVfbmFsbHNfc21yX2hpdHMkcHJvYmVJRCwKICBmZW1hbGU9ZmVtYWxlX25hbGxzX3Ntcl9oaXRzJHByb2JlSUQKKSwgZmlsbCA9IGMoImdyYXk4MCIsICJsaWdodGJsdWUiLCAibGlnaHRwaW5rIikpCgpkaXNwbGF5X3Zlbm4obGlzdCgKICBjcm9zcz1jcm9zc19zbXJfaGl0c19oZWlkaSRwcm9iZUlELAogIG1hbGU9bWFsZV9uYWxsc19zbXJfaGl0c19oZWlkaSRwcm9iZUlELAogIGZlbWFsZT1mZW1hbGVfbmFsbHNfc21yX2hpdHNfaGVpZGkkcHJvYmVJRAopLCBmaWxsID0gYygiZ3JheTgwIiwgImxpZ2h0Ymx1ZSIsICJsaWdodHBpbmsiKSkKCmRpc3BsYXlfdmVubihsaXN0KAogIGNyb3NzPWNyb3NzX3Ntcl9oaXRzJHByb2JlSUQsCiAgbWFsZT1tYWxlX2JsYXVfc21yX2hpdHMkcHJvYmVJRCwKICBmZW1hbGU9ZmVtYWxlX2JsYXVfc21yX2hpdHMkcHJvYmVJRAopLCBmaWxsID0gYygiZ3JheTgwIiwgImxpZ2h0Ymx1ZSIsICJsaWdodHBpbmsiKSkKCmRpc3BsYXlfdmVubihsaXN0KAogIGNyb3NzPWNyb3NzX3Ntcl9oaXRzX2hlaWRpJHByb2JlSUQsCiAgbWFsZT1tYWxlX2JsYXVfc21yX2hpdHNfaGVpZGkkcHJvYmVJRCwKICBmZW1hbGU9ZmVtYWxlX2JsYXVfc21yX2hpdHNfaGVpZGkkcHJvYmVJRAopLCBmaWxsID0gYygiZ3JheTgwIiwgImxpZ2h0Ymx1ZSIsICJsaWdodHBpbmsiKSkKCgpkaXNwbGF5X3Zlbm4obGlzdCgKICBtYWxlX25hbGxzPW1hbGVfbmFsbHNfc21yX2hpdHNfaGVpZGkkcHJvYmVJRCwKICBmZW1hbGVfbmFsbHM9ZmVtYWxlX25hbGxzX3Ntcl9oaXRzX2hlaWRpJHByb2JlSUQsCiAgbWFsZV9ibGF1PW1hbGVfYmxhdV9zbXJfaGl0cyRwcm9iZUlELAogIGZlbWFsZV9ibGF1PWZlbWFsZV9ibGF1X3Ntcl9oaXRzJHByb2JlSUQKKSwgZmlsbCA9IGMoImxpZ2h0Ymx1ZTIiLCAibGlnaHRwaW5rMiIsImxpZ2h0Ymx1ZTQiLCJsaWdodHBpbms0IikpCgpkaXNwbGF5X3Zlbm4obGlzdCgKICBtYWxlX25hbGxzPW1hbGVfbmFsbHNfc21yX2hpdHNfaGVpZGkkcHJvYmVJRCwKICBmZW1hbGVfbmFsbHM9ZmVtYWxlX25hbGxzX3Ntcl9oaXRzX2hlaWRpJHByb2JlSUQsCiAgbWFsZV9ibGF1PW1hbGVfYmxhdV9zbXJfaGl0c19oZWlkaSRwcm9iZUlELAogIGZlbWFsZV9ibGF1PWZlbWFsZV9ibGF1X3Ntcl9oaXRzX2hlaWRpJHByb2JlSUQKKSwgZmlsbCA9IGMoImxpZ2h0Ymx1ZTIiLCAibGlnaHRwaW5rMiIsImxpZ2h0Ymx1ZTQiLCJsaWdodHBpbms0IikpCmBgYApgYGB7cn0KdG9fcGxvdCA8LSByYmluZChjcm9zc19zbXJfaGl0cyxtYWxlX25hbGxzX3Ntcl9oaXRzLGZlbWFsZV9uYWxsc19zbXJfaGl0cywgbWFsZV9ibGF1X3Ntcl9oaXRzLGZlbWFsZV9ibGF1X3Ntcl9oaXRzKQpnZ3Bsb3QodG9fcGxvdCAlPiUgbXV0YXRlKGBIRUlESSBUZXN0YD1pZmVsc2UocF9IRUlESSA+IDAuMDEsIlBhc3NlZCIsIkZhaWxlZCIpKSAlPiVuYS5vbWl0KCksYWVzKHNldCxmaWxsPWBIRUlESSBUZXN0YCkpKwogIGdlb21fYmFyKCkgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiUGFzc2VkIj0iZ3JheTIwIiwiRmFpbGVkIj0iZ3JheTcwIikpKwogIGdlb21fdGV4dChzdGF0PSJjb3VudCIscG9zaXRpb249InN0YWNrIixtYXBwaW5nPWFlcyhsYWJlbD0uLmNvdW50Li4pLHZqdXN0PS0wLjI1KSsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsNzUpKSArCiAgbGFicyh5PSJOdW1iZXIgb2YgRE5BbS1tZWRpYXRlZFxuQXNzb2NpYXRpb25zIix4PSIiKSsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT02MCxoanVzdD0xLHZqdXN0PTEpKQpgYGAKCmBgYHtyfQpjcm9zc19zbXJfaGl0c1tpcy5uYShwX0hFSURJKV0KYGBgCgo=